package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author mqx
 * @date 2021-2-26 09:44:30
 */
@Service
public class CartServiceImpl implements CartService {

    @Autowired
    private CartInfoMapper cartInfoMapper;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private CartAsyncService cartAsyncService;

    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {
        /*
            1.  先判断当前购物车中是否有该商品
                zs  1001  1 + 1
                ls  1001  1 + 1
                select * from cart_info where sku_id=skuId and user_id=userId
                true: 则商品数量相加
                false: 直接添加到数据库
            2.  还需要存储在缓存中
         */
        //  操作缓存，必须了解使用的数据类型，以及key！ 使用hash 存储
        //  hset(key,field,value); hget(key,field);key=user:userId:cart  field = skuId  value=cartInfo;
        //  定义购物车key
        String cartKey = this.getCartKey(userId);
        //  判断缓存中是否有购物车的key
        if (!redisTemplate.hasKey(cartKey)){
            //  从数据查询到数据，并放入缓存
            this.loadCartCache(userId);
        }
        //  代码在走到这个位置的时候：缓存中一定有数据！
        //        QueryWrapper<CartInfo> queryWrapper = new QueryWrapper<>();
        //        queryWrapper.eq("sku_id",skuId);
        //        queryWrapper.eq("user_id",userId);
        //        CartInfo cartInfoExist = cartInfoMapper.selectOne(queryWrapper);
        //  可以直接查询缓存！
        //  hget(key,field);
        CartInfo cartInfoExist = (CartInfo)redisTemplate.opsForHash().get(cartKey,skuId.toString());

        //  说明添加的商品在购物车中存在
        if (cartInfoExist!=null){
            //  数量相加
            cartInfoExist.setSkuNum(cartInfoExist.getSkuNum()+skuNum);
            //  给实时价格赋值
            cartInfoExist.setSkuPrice(productFeignClient.getSkuPrice(skuId));
            //  修改更新时间
            cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
            //  如果第一次添加 默认被选中1 ，用户更改状态 0 ，此时用户第二次添加，这个使用
            cartInfoExist.setIsChecked(1);
            //  将修改之后的数据保存到数据库
            //  cartInfoMapper.updateById(cartInfoExist);
            cartAsyncService.updateCartInfo(cartInfoExist);
            //  还需要操作缓存.....
        }else {
            //  直接添加到购物车中
            CartInfo cartInfo = new CartInfo();
            //  赋值cartInfo
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            cartInfo.setCartPrice(skuInfo.getPrice());
            //  实时价格初始化赋值
            cartInfo.setSkuPrice(skuInfo.getPrice());
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
            cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));

            //  cartInfoMapper.insert(cartInfo);
            cartAsyncService.saveCartInfo(cartInfo); // 给其他线程执行了。跳过了这个地方，id 主键自增。
            //  操作缓存！
            cartInfoExist=cartInfo;

        }

        //  将数据放入缓存
        redisTemplate.opsForHash().put(cartKey,skuId.toString(),cartInfoExist);
        //  设置购物车的过期时间：灵活代码！
        this.setCartKeyExpire(cartKey);
    }

    @Override
    public List<CartInfo> getCartInfoList(String userId, String userTempId) {
        List<CartInfo> cartInfoList = new ArrayList<>();
        //  判断userId 登录的用户Id
        if (StringUtils.isEmpty(userId)){
            //  应该根据临时用户Id 查询数据
            cartInfoList = this.getCartInfoList(userTempId);
        }
        //  判断
        if(!StringUtils.isEmpty(userId)){
            cartInfoList = this.getCartInfoList(userId);
        }

        return cartInfoList;
    }

    @Override
    public List<CartInfo> getCartInfoList(String userId) {
        //  声明一个集合
        List<CartInfo> cartInfoList = new ArrayList<>();
        //  判断一下传递过来的参数是否为空
        if (StringUtils.isEmpty(userId)) return cartInfoList;
        //  不为空的情况下： 添加购物车的时候，同步的缓存，异步的数据库。 缓存中有数据
        //  从缓存中获取数据
        //  存储的hash 数据类型
        String cartKey = this.getCartKey(userId);
        //  hset(key,field,value) hget(key,field);只是获取到某一个数据 并发所有！
        //  hvals(key);
        cartInfoList = redisTemplate.opsForHash().values(cartKey);
        //  判断
        if (!CollectionUtils.isEmpty(cartInfoList)){
            //  是否需要对这个集合进行操作呢? 需要进行排序 按照商品的更新时间进行排序！
            cartInfoList.sort(new Comparator<CartInfo>() {
                @Override
                public int compare(CartInfo o1, CartInfo o2) {
                    return DateUtil.truncatedCompareTo(o2.getUpdateTime(),o1.getUpdateTime(), Calendar.SECOND);
                }
            });
            return cartInfoList;
        }else {
            //  如果缓存中没有数据的时候，{有时候缓存到期了} 从数据库获取数据。
            cartInfoList = this.loadCartCache(userId);
            return cartInfoList;
        }
    }
    //  根据用户Id 查询数据库的购物车列表，并且放入缓存。
    private List<CartInfo> loadCartCache(String userId) {
        //  通过用户Id 查询数据库中的数据
        //  select * from cart_info where user_id = ?
        List<CartInfo> cartInfoList = cartInfoMapper.selectList(new QueryWrapper<CartInfo>().eq("user_id", userId));
        //  如果是空，直接返回
        if (CollectionUtils.isEmpty(cartInfoList)) return cartInfoList;

        //  从数据库查询出来之后，同样按照更新时间进行排序
        //        cartInfoList.sort(new Comparator<CartInfo>() {
        //            @Override
        //            public int compare(CartInfo o1, CartInfo o2) {
        //                return DateUtil.truncatedCompareTo(o2.getUpdateTime(),o1.getUpdateTime(), Calendar.SECOND);
        //            }
        //        });

        //  hset(key,field,value); hmset(key,vlaue);
        //  获取购物车的key
        String cartKey = this.getCartKey(userId);
        //  声明一个map
        HashMap<String, CartInfo> cartInfoHashMap = new HashMap<>();
        //  将集合循环并放入缓存
        for (CartInfo cartInfo : cartInfoList) {
            //  细节：前提是缓存中没有数据，有可能商品价格会发生变化。
            cartInfo.setSkuPrice(productFeignClient.getSkuPrice(cartInfo.getSkuId()));
            //  单个赋值
            //  redisTemplate.opsForHash().put(cartKey,cartInfo.getSkuId(),cartInfo);
            cartInfoHashMap.put(cartInfo.getSkuId().toString(),cartInfo);
        }
        //  一次性赋值全部
        redisTemplate.opsForHash().putAll(cartKey,cartInfoHashMap);
        //  重新设置一下过期时间
        this.setCartKeyExpire(cartKey);

        return cartInfoList;
    }

    //  设置购物车的过期时间
    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    //  定义购物车的key
    private String getCartKey(String userId) {
       return RedisConst.USER_KEY_PREFIX+userId+RedisConst.USER_CART_KEY_SUFFIX;
    }
}
